package ioc

import (
	"errors"
	"fmt"
	"gitee.com/youbeiwuhuan/gin-practice/go-ioc/ioc/lifecycle"
	cmap "github.com/orcaman/concurrent-map/v2"
	"sync"
)

var (
	container = cmap.New[any]()
	lock      sync.Mutex
)

// RegisterSingleton 注册单例的bean
func RegisterSingleton[T any](id string, bean *T) error {
	lock.Lock()
	defer lock.Unlock()

	_, exist := container.Get(id)
	if exist {
		return errors.New("id:[" + id + "] exist!")
	}

	//  判断相同类型是否存在
	for item := range container.IterBuffered() {
		if Is[T](item.Val) {
			fmt.Printf("Key: %s, Val: %v\n", item.Key, item.Val)
			return errors.New("bean same type exist!")
		}
	}

	container.Set(id, bean)
	afterRegister(bean)

	return nil
}

// RegisterMulti 注册非单例的bean
func RegisterMulti[T any](id string, bean *T) error {
	lock.Lock()
	defer lock.Unlock()

	_, exist := container.Get(id)
	if exist {
		return errors.New("id:[" + id + "] exist!")
	}

	// 不要与单例bean冲突
	_, err := GetByType[T]()
	if err != nil {
		return err
	}

	container.Set(id, bean)

	afterRegister(bean)

	return nil
}

func afterRegister(bean any) {
	if Is[lifecycle.InitializingBean](bean) {
		initBean, _ := ToType[lifecycle.InitializingBean](bean)
		initBean.AfterRegister()
	}
}

// GetById 根据id获取bean
func GetById[T any](id string) (*T, error) {
	v, ok := container.Get(id)
	if !ok {
		return (interface{})(nil).(*T), errors.New("id:[" + id + "] does not  exist!")
	}

	vt, ok := ToType[*T](v)
	if ok {
		return vt, nil
	}

	return (interface{})(nil).(*T), errors.New("id:[" + id + "] Bean type not match!")
}

// GetByType 根据类型查找,仅可以获取单例bean
func GetByType[T any]() (*T, error) {

	exist := false
	var r *T
	for item := range container.IterBuffered() {

		if Is[T](item.Val) {
			//fmt.Printf("Key: %s, Val: %v\n", item.Key, item.Val)
			if !exist {
				r, _ = ToType[*T](item.Val)
				exist = true
			} else {
				return (interface{})(nil).(*T), errors.New("exist mulity bean for this type")
			}
		}

	}

	if !exist {
		return (interface{})(nil).(*T), errors.New("Bean for this type does not exist!")
	}

	return r, nil
}

// Shutdown 销毁容器
func Shutdown() {
	for item := range container.IterBuffered() {
		func(bean interface{}) {
			defer func() {
				if r := recover(); r != nil {
					fmt.Println("Shutdown recovered:", r)
				}
			}()

			if Is[lifecycle.DisposableBean](bean) {
				detroyBean, _ := ToType[lifecycle.DisposableBean](bean)
				detroyBean.Destroy()
			}
		}(item.Val)
	}
}
